package jpave.annotator;

import java.io.*;
import java.util.Vector;
import java.util.zip.GZIPInputStream;
import java.sql.ResultSet;

import assem.DBConn;
import jpave.annotator.CoreAnnotator;
import jpave.annotator.DoBlast;
import jpave.annotator.DoUniProt;
import jpave.database.CoreDBWrapper;
import jpave.database.OverviewDBWrapper;
import jpave.dataholders.BlastHitData;
import util.methods.CommandLineParser;
import util.methods.FileHelpers;
import util.methods.HostsCfg;
import util.methods.TimeHelpers;
import util.methods.Version;
import util.methods.PaveProps;
import util.methods.PropType;
import util.methods.BlastArgs;
import util.Debug.ErrorReport;

/** 
* annotate a specified dataset using its sTCW.cfg parameters.
*/
public class CoreMain 
{	
	public static final String aPAVE_VERSION_STRING = "TCW annotation v" + 
			Version.strPAVEver + " " + Version.strRelDate;
	public static boolean debug = false;
	
	private static final String logDir = "logs";
	private static final String logFile = "annotator.log";
	private static final String ProjDirName = "projects";
	private static final String OverviewFile = "overview.txt";
	private static final String DBfastaDirName = "DBfasta";
	private static final String PAVEcfg = "PAVE.cfg";
	private static final String mTCWcfg = "sTCW.cfg";
	private static String uniBlast = "uniblasts";  // unitrans are written and blasts occur
	public static final int numDB = 20; // zero plus 1-10, used in DoBlast
	
	// defaults are set here instead of PaveProps.java so can be checked to see if User set
	public static final int defaultPairsLimit = 10000;
	private static final int defaultUniTransType = 0; // ctg+sng
	private static final int defaultFlankingRegion = 30;
	private static final String defaultTaxo = "Unk";
	private static final int defaultRepOverlap = 95;
	private static final int defaultRepSimilarity = 97;
	private static int minBitScore = 0;
	public static int getBitScore() { return minBitScore;} // BlastHitData
	
	public static void main(String[] args)
	{   
		//System.err.println("Starting with: \n" + Runtime.getRuntime().freeMemory() + " bytes free");
		//System.err.println((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + " bytes used");
		if (!Version.checkJavaSupported())
			System.exit(-1);
			
		/**  Parameter processing */
		if (CommandLineParser.hasOption(args, "-h") 
				|| CommandLineParser.hasOption(args, "-help") 
				|| CommandLineParser.hasOption(args, "help")
				|| args.length == 0) {
			Usage_exit();
		}
		long nAnnoStart = TimeHelpers.getTime();
		
		projName = args[0]; 
		projPath = getProjectPath(projName);
		if (projPath == null) Usage_exit();
		System.err.println("\n----- " + aPAVE_VERSION_STRING + " -- project " + projName + " -----");
		
	// Determin action
		bdoAnno=true; // if false, doesn't print keywords if only doGO or doOV
		boolean doGO = false; // 1. new annoDBs, 2. no new but no GO, 3. -G flag 
		boolean doOV=true,  doWeb = false, doSchema=false, doDeleteAnno=false;
		
		if (CommandLineParser.hasOption(args, "-p"))
			Parameter_exit();	
				
		if(CommandLineParser.hasOption(args, "-d"))
			debug = true;
		
		if(CommandLineParser.hasOption(args, "-q"))
			noPrompt = true;		

		if(CommandLineParser.hasOption(args, "-s")) { // only needed for testing
			System.err.println("Update summary only");
			bdoAnno=false; doGO = false; doWeb=false; 
			doOV = true;
		}
		if(CommandLineParser.hasOption(args, "-G")) { // GO only (annotation already done)
		    System.err.println("Computing GOs only");
			bdoAnno=false;
			doGO = true;
		}
		else if(CommandLineParser.hasOption(args, "-GG")) { // annotation + GO
	        System.err.println("Computing annotation and GOs");
			bdoAnno=true;
			doGO = true;
		}	
		else if(CommandLineParser.hasOption(args, "-W")) { 
	        System.err.println("Computing web GO tables only");
			bdoAnno = false; doOV = false;
			doWeb = true;
		}			
		else if(CommandLineParser.hasOption(args, "-SCHEMA")) { 
		    System.err.println("Check/update schema");
		    doSchema = true;
		}
		else if(CommandLineParser.hasOption(args, "-a")) { 
		    System.err.println("Delete annotation");
		    doDeleteAnno = true;
		}				
		System.err.println("");
		
		// Read HOSTS.cfg
		hosts = new HostsCfg();
		
		// 1. create log file
		if (bdoAnno || doGO)
			if (! createLogFile()) Usage_exit();
		
		// 2. create objects used for annotation
		uniObj = new DoUniProt();
		annoObj = new CoreAnnotator();	
		blastObj = new DoBlast();

		// 3. read LIB.cfg and sTCW.cfg
	
		if (! cfgPAVEload()) 
			die("error loading " + mTCWcfg);		

		// update schema - only for testing
		if (doSchema) {
		    Version.updateSchema(hosts.host, pave_db, hosts.user, hosts.pass);	
		    return;
		}
		
		// 4. create database connection
		try {
			if (mProps.getProperty("JPAVE_SKIP_ANNOTATE").equals("1")) bdoAnno=false;
			
			Version.updateSchema(hosts.host, pave_db, hosts.user, hosts.pass);
			sqlObj = new CoreDBWrapper(bdoAnno, hosts.host, pave_db, hosts.user, hosts.pass, assemblyID);
			sqlObj.setIsProteinDB();
			isProtein = sqlObj.isProteinDB();
		}
		catch (Exception e) {ErrorReport.die(e, "Error creating database connection for " + pave_db);}
		
		annoObj.setMainObj(uniObj, blastObj, sqlObj);
		uniObj.setMainObj(annoObj, blastObj, sqlObj);
		blastObj.setMainObj(uniObj, annoObj, sqlObj);		
		
		/** 5. all checks **/
		try
		{
			if (doDeleteAnno) {
				sqlObj.deleteAnnotation();
				System.exit(0);
			}
			if (bdoAnno) // not just doing GO or summary
			{
				// 5. test files
				File path = new File(uniBlastDir);
				// if a reannotaton, don't blow away blast files
				// if new database, then ask whether to blow them away
				// if running prompt-free, don't remove them
				if (sqlObj.isFirstAnno() && !sqlObj.isReAnnotate() && path.exists() && !noPrompt) {
					if (yesNo("Delete existing blast results directory " + removeCurrentPath(uniBlastDir))) {
						System.err.println("Deleting directory...");
						if (FileHelpers.deleteDir(uniBlastDir))
							Print("Deleted existing " + uniBlastDir);
						else 
							PrtWarn("Could not delete " + uniBlastDir);
					}
				}
				if (!blastObj.testFilesAndcreateOutFiles()) {
					die(" failed testing files");
				}
				boolean firstAnno = sqlObj.isFirstAnno();
				boolean doAnnoDB = (blastObj.numDB() > 0) ? true : false;
				boolean doSelf =    blastObj.doPairs();
				boolean hasGOtree = sqlObj.existsGOtree();
				
				if (!firstAnno && !doAnnoDB && !doSelf) { // CAS 2/13/15 add GC/ORF check
					Print("No annoDB annotation to be done\n");
					if (yesNo("Perform GC and ORF computations?")) bdoAnno=true;
					else bdoAnno = false;
				}
				doGO = false;
				if (doAnnoDB || !hasGOtree) { 
					Print("Check GO tree ");
					if (checkGODB(false)) doGO=true;
					else cntPrompts++; // msg was written about no go_tree, do they want to continue
				}
				
				if (cntPrompts> 0) {
					Boolean ans = yesNo("Please confirm above parameters. Continue with annotation? ");
					if (!ans) 
						die("by user request");
				}
				
				try {
					if (bDelAnno) sqlObj.deleteAnnotation();
					else sqlObj.deleteSpecies();
				}
				catch (Exception e) {die("error deleting annotation");}			
			}
			else { // -G set
				if (doGO) checkGODB(true);
			}
			System.out.flush();
			System.out.println("---------------------------------------");
			System.out.flush();
		}
		catch (Exception e) {}

		/* Finally do executions */
		if (bdoAnno) {
			BlastHitData.startHitWarnings(projName);
			if (! annoObj.run(getProjectPath(projName))) 
				die("error in running annotation");
		}
		if (doGO)
		{
			String godb = mProps.getProperty("JPAVE_GO_DB").trim(); 
			String goslim = mProps.getProperty("JPAVE_GO_SLIM").trim(); 
			String slimfile = mProps.getProperty("JPAVE_SLIM_FILE").trim(); 
			GO.addJPaveGOTables(sqlObj,godb,assemblyID, goslim, slimfile); 
		}

		if (doOV)
		{
			// 8. Create Overview and write to file
			try {
				OverviewDBWrapper viewObj = new 
						OverviewDBWrapper(sqlObj.getDBconnection());	
				String oview = viewObj.createOverview(new Vector <String> () );
				writeOverview(oview);
			}
			catch (Exception e) {}
		}	
		
		if (doWeb)
		{
			Web.fillWebViewTable(sqlObj, assemblyID);
			Web.fillWebGOViewTable(sqlObj, assemblyID);
		}
		
		CoreMain.PrtDateMsgTime("\nEnd annotation for " + projName, nAnnoStart);
	}

	/**********************************************************************/
	/***
	 * load PAVE.cfg for annotator, calls PaveProps
	 */
	static private boolean cfgPAVEload() 
	{			
		String cfgFile = getProjectCfgFilePath(projName);
		File cfgFileObj = new File(cfgFile);
		if (! cfgFileObj.exists()) {
			PrtError("Could not find configuration file " + cfgFile);
			return false;
		}
		
        HostsCfg hosts = new HostsCfg();

		// read PAVE.cfg
		mProps = new PaveProps(PropType.Annotate);
		try {
			mProps.loadAnnotate(cfgFileObj);
			cfgNonFileParams();
			cfgSelfblastParams();
			cfgDBParams();
		} catch (Exception err) {
			ErrorReport.reportError(err, "reading configuration file " + cfgFile);
			return false;
		}		
		return true;
	}
	
	static private void cfgNonFileParams() {
		try {
			pave_db = mProps.getProperty("PAVE_db");
			if (pave_db==null) CoreMain.die("sTCW.ctg does not contain PAVE_db");
			
			assemblyID = mProps.getProperty("AssemblyID");		
			nCPUs = Integer.parseInt(mProps.getProperty("CPUs"));	
			Print("CPUs/Threads = " + nCPUs); // CAS 1/30/15
						
			// blast directory depends on if its comparing against all, ctg or sng
			int type = Integer.parseInt(mProps.getProperty("JPAVE_unitrans_type"));

			if (type != -1 && bdoAnno) {
				if (type==0)
					Print("type = 0 (contigs + singletons only)");
				else if (type == 1) 
					Print("type = 1 (contigs only)");
				else if (type == 2)
					Print("type = 2 (singletons only)");
				else {
					Print("--- invalid JPAVE_unitrans_type. Must be 0, 1, or 2. Assume 0.");
					type = 0;
				}
				blastObj.setUniTransType(type);
			}
			int bit = Integer.parseInt(mProps.getProperty("JPAVE_min_bitscore"));
			if (bit != -1) {
				minBitScore = bit;
				Print("min_bitscore = " + bit);
			}
			
			String path = getProjectPath(projName);		
			uniBlastDir = path + "/" + uniBlast;
			blastObj.setUniBlastDir(uniBlastDir);
			String prefix = uniBlastDir + "/" + assemblyID + "_";
			blastObj.setUniBlastPrefix(prefix);
		}
		catch (Exception e) {
			ErrorReport.reportError(e, "Error getting parameters");
		}
	}
	
	static private void cfgSelfblastParams() {		
		try {			
			int pairs = Integer.parseInt(mProps.getProperty("JPAVE_pairs_limit"));
			String selfBlastFile =  mProps.getProperty("JPAVE_unitrans_selfblast");
			String tselfBlastFile =  mProps.getProperty("JPAVE_unitrans_tselfblast");
			String selfBlastArgs = mProps.getProperty("JPAVE_selfblast_args");
			String tselfBlastArgs = mProps.getProperty("JPAVE_tselfblast_args");
			
			// a '-' or -1 indicates that the keyword was not present
			
			if (pairs == -1 && selfBlastFile.equals("-") && tselfBlastFile.equals("-")) {
				annoObj.setMaxDPpairs(0);
				return;
			}
	
			if (pairs != -1) {if (bdoAnno) Print("pairs_limit = " + pairs); }
			else pairs = defaultPairsLimit;
			annoObj.setMaxDPpairs(pairs);
			
			// SELFBLAST
			if (!selfBlastFile.equals("-")) { 
				if (bdoAnno) Print("selfblast file = " + selfBlastFile);
				
				if (selfBlastFile.equals("")) { 		// run megablast
					if (selfBlastArgs.equals("")) {
					    if (isProtein) selfBlastArgs = BlastArgs.getBlastnOptions();
					    else selfBlastArgs = BlastArgs.getBlastpOptions();
					}
					else if (bdoAnno) Print("selfblast args = " + selfBlastArgs);
					blastObj.setSelfBlastArgs(selfBlastArgs);
					BlastArgs.setBlastnOptions(selfBlastArgs);
				}
				else {
					if (! selfBlastFile.startsWith("/")) // user supplied blast file
						selfBlastFile = uniBlastDir + "/" + selfBlastFile;
					blastObj.setSelfBlastFile(selfBlastFile);
				}
			} // no selfblast
			
			// TSELFBLAST
			if (!tselfBlastFile.equals("-")) { 
				if (bdoAnno) Print("tselfblast file = " + tselfBlastFile);
				
				if (tselfBlastFile.equals("")) {        // run tblastx (can't be proteinDB)					
					if (tselfBlastArgs.equals("")) tselfBlastArgs = BlastArgs.getTblastxOptions();
					else if (bdoAnno) Print("tselfblast args = " + tselfBlastArgs);	
					blastObj.setTSelfBlastArgs(tselfBlastArgs);
		            BlastArgs.setTblastxOptions(tselfBlastArgs);
				}
				else {
					if (! tselfBlastFile.startsWith("/"))  // user supplied blast file
						tselfBlastFile = uniBlastDir + "/" + tselfBlastFile;
					blastObj.setTSelfBlastFile(tselfBlastFile);
				}
			}
		}
		catch (Exception e) {
			ErrorReport.reportError(e, "Error getting Selfblast parameters");
		}		
	}
	
	static private void cfgDBParams() {
		try {
			int flank = Integer.parseInt(mProps.getProperty("JPAVE_flanking_region"));		
			if (flank == -1) flank = defaultFlankingRegion;
			else Print("flanking_region = "  + flank);				
			uniObj.setFlankingRegion(flank);
			
			// if no number at end
			cfgAddDB(0, "JPAVE_uniprot_blast", "JPAVE_uniprot_fasta", 
					"JPAVE_uniprot_args", "JPAVE_uniprot_taxo", "JPAVE_DBdate", 
					"JPAVE_unitrans_subset", "JPAVE_search_pgm",
					 null);
			
			for (int i=1; i < numDB; i++) {
				String n = Integer.toString(i);
				String b = "JPAVE_unitrans_DBblast_" + n;
				String f = "JPAVE_DBfasta_" + n;
				String a = "JPAVE_DBargs_" + n;
				String t = "JPAVE_DBtaxo_" + n;
				String d = "JPAVE_DBdate_" + n;
				String s = "JPAVE_unitrans_subset_" + n;
				String p = "JPAVE_DBsearch_pgm_" + n;

				cfgAddDB(i, b, f, a, t, d, s, "JPAVE_DBdate", p);
			}
		}
		catch (Exception e) {
			ErrorReport.reportError(e, "getting annoDB parameters");
		}
	}
	
	static private void cfgAddDB (int index, String mblast, String mfasta, String margs, 
			String mtaxo, String mdate, String mset, String mdefdate, String mpgm) {
		try {
			String blast =   mProps.getProperty(mblast);	
			String DBfasta = mProps.getProperty(mfasta); 
			String args =    mProps.getProperty(margs);
			String taxo =	 mProps.getProperty(mtaxo);
			String DBdate =  mProps.getProperty(mdate);
			String subset =  mProps.getProperty(mset);
			String searchPgm = mProps.getProperty(mpgm);
				
			if (keyExist(blast) || keyExist(DBfasta)) 
			    if (bdoAnno) Print("DB#" + index);
			
			// hits tabular file provided
			if (keyExist(blast)) { // supplied file
				if (bdoAnno) Print(mblast + " = " + blast);
				if (! blast.startsWith("/"))  
					blast = uniBlastDir + "/" + blast;
				
				if (subset.equalsIgnoreCase("yes") || subset.equals("1")) {
					CoreMain.die("Cannot request subset of sequences and supply blast file - fix sTCW.cfg");
				}
				
				if (!keyExist(DBfasta)) 
					PrtWarn("No database fasta file supplied; DB entries will not have descriptions or sequences"); 
			}
			
			// DBfasta file
			if (keyExist(DBfasta)) { 
				if (bdoAnno) Print(mfasta + " = " + DBfasta);
				if (! DBfasta.startsWith("/"))  {
					String path = getProjectsPath();		
					DBfasta = path + "/" + DBfastaDirName + "/" + DBfasta;
				}
				if (!args.equals("-") && !args.equals("")) {
				    if (bdoAnno) Print(margs + " = " + args);
				}
				else args = ""; // don't know if nucleotide annoDB at this point
				if (!searchPgm.equals("")) {
					Vector <String> pgms = BlastArgs.getSearchPgms();
					if (!pgms.contains(searchPgm)) {
						searchPgm="";
						pgms = BlastArgs.getValidSearchPgmVec();
						if (pgms.contains(searchPgm)) 
							CoreMain.PrtWarn("No path provided for search program " + searchPgm);
						else {
							CoreMain.PrtWarn("Not a valid search program identifier " + searchPgm);
							CoreMain.PrtSpMsg(3, "The following are valid: ");
							for (String x : pgms) CoreMain.PrtSpMsg(4, x);
						}
					}
				}
			}
			
			if (blast.equals("-") && DBfasta.equals("-")) return;
			
			// taxo
			if (!taxo.equals("-")) {
			    if (bdoAnno) Print(mtaxo + " = " + taxo);
			}
			else taxo = defaultTaxo;
			
			// DBfasta date
			if (keyExist(DBdate)) {
				if (bdoAnno) Print(mdate + "=" + DBdate);
			}
			else {
				if (mdefdate != null) DBdate =  mProps.getProperty(mdefdate);
				if (!keyExist(DBdate)) DBdate = null;
			}
			if (DBdate != null) {
				if (!goodDate(DBdate)) die("The date " + DBdate + " is not formated yyyy-mm-dd (e.g. 2011-12-31)");
			}
		
			// subset
			boolean bsubset = false;
			if (subset.equalsIgnoreCase("yes") || subset.equals("1")) {
				if (bdoAnno) Print(mset + " = yes");
				bsubset = true;
			}		
			blastObj.makeDB(index, blast,DBfasta, args, taxo, DBdate, bsubset, searchPgm);
		}
		catch (Exception e) {
			String s = "getting parameter: " + mblast + " " + mfasta + " " + margs + " " + mset;
			ErrorReport.reportError(e, s);
		}
	}
	
	static private boolean keyExist(String x) {
		if (x != null && !x.equals("-") && !x.equals("")) return true;
		else return false;
	}

	static private boolean goodDate (String dt) { // yyyy-mm-dd
		int f = dt.indexOf("-");
		int l = dt.lastIndexOf("-");
		if (f== -1 || l == -1 || f == l) return false;
		String year = dt.substring(0,f);
		String month = dt.substring(f+1,l);
		String day = dt.substring(l+1, dt.length());
		if (year.length() != 4 || month.length() != 2 || day.length() != 2) return false;

		int y = Integer.parseInt(year);
		if (y<1000 || y > 3000) return false;
		int m = Integer.parseInt(month);
		if (m < 1 || m > 12) return false;
		int d = Integer.parseInt(day);
		if (d < 1 || d > 31) return false;
	
		return true;
	}
	/************************************************************************/
	/**
	 * Create annotation log and error files
	 */
	private static boolean createLogFile() 
	{	    	
		File projDir = new File(getProjectPath(projName));
		if (!projDir.exists()) {
			System.err.println("*** Error: Could not find project '" + projName + "'.");
			return false;
		}
	    	
		// Make sure log directory exists
		File logDir = new File(getProjectLogPath(projName));
		if (!logDir.exists()) {
			System.err.println("*** Error: Could not find project log directory, creating it ...");
			if (!logDir.mkdir()) {
				System.err.println("*** Failed to create project log directory '" + 
						logDir.getAbsolutePath() + "'.");
				return false;
			}
		}
	    	
		// Create log file
		String logFullName = getProjectLogFilePath(projName);
		File logFile = new File (logFullName);
		
		// save last 10 logfiles
		if (logFile.exists()) {
			String logN;
			File fileN;
			boolean success;
			
			for (int i=10; i >= 1; i--) {
				logN = logFullName + "." + Integer.toString(i);
				fileN = new File(logN);
				if (fileN.exists()) {
					logN = logFullName + "." + Integer.toString(i+1);
					File fileNx = new File (logN);
					success = fileN.renameTo(fileNx);
					if (!success) System.err.println("Warning: could not move " + 
							fileN.getName() + " to " + fileNx.getName());
				}
			}
			logN = logFullName + ".1";
			fileN = new File(logN);
			success = logFile.renameTo(fileN);
			if (!success) System.err.println("Warning: could not move " + logFile.getName()
					+ " to " + fileN.getName());	
		}
		
		try {
			System.err.println("Log file:   " + removeCurrentPath(logFile.getAbsolutePath()));
			FileOutputStream out = new FileOutputStream(logFile.getAbsolutePath());
			logFileObj = new PrintWriter(out); 
		}
		catch (Exception e)
        {
            ErrorReport.reportError(e, "Error writing to " + logFile.getAbsoluteFile());
            return false;
        }
		return true;
	}
	/***************************************************************************
	 * Returns paths to various directories and files used by the annotator
	 ***************************************************************************/

	// Find where /projects is so can execute from jpave directory or one up
	static private String getTopLevelPath()
	{
		try {
			String s = ProjDirName;
			File f = new File(s);
			if (f.exists()) {
				return new File(".").getCanonicalPath();
			}
			s = "../" + ProjDirName;
			f = new File(s);
			if (f.exists()) {
				return new File("../").getCanonicalPath();
			}
			PrtError("Cannot find project directory " + ProjDirName);
			return null;
		}
		catch (Exception e) {
			return null;
		}
	}
	static public String getProjectLogFilePath(String projectName)
	{
		return getProjectLogPath(projectName) + "/" + logFile;
	}
	static public String getProjectLogPath(String projectName)
	{
		return getProjectPath(projectName) + "/" + logDir ;
	}	
	static public String getProjectsPath() {
		return getTopLevelPath() + "/" + ProjDirName;
	}	
	static public String getProjectPath(String projectName) {
		return getTopLevelPath() + "/" + ProjDirName + "/" + projectName;
	}	
	static public String getProjectCfgFilePath(String projectName) {
	    String p = getProjectPath(projectName);
	    String f = p + "/" + PAVEcfg;
	    File h = new File(f);
	    if (h.exists()) {
	        Print("\nReading " + PAVEcfg);
	        return f;
	    }
	    Print("\nReading " + mTCWcfg);
		return getProjectPath(projectName) + "/" + mTCWcfg;
	}
	///////////////////////////////////////////////////////////////
	private static void writeOverview(String text) {
		String overFile = getProjectPath(projName) + "/" + logDir + "/" + OverviewFile;
		try {
			//if (!new File(overFile).exists()) return; // CAS 2/7/15
			System.err.println("Writing overview file: " + removeCurrentPath(overFile));
			FileOutputStream out = new FileOutputStream(overFile);
			PrintWriter fileObj = new PrintWriter(out); 
    			fileObj.println(text);
    			fileObj.close();
		}
		catch (Exception e)
        {
            ErrorReport.reportError(e, "Error writing to " + overFile);
        }

	}
	 /************************************************************************/
	 /************************************************************************
     * Methods for printing to logFile, errFile and stdout
     **************************************************************************/
  
	static public void Print(String s) {
	    System.err.println(s);
	    if (logFileObj != null) { // not open if called by viewPAVE/Version
	    		logFileObj.println(s);
	        logFileObj.flush();
	    }
	}	
	static public void PrtError(String msg)
	{
	    	String s = "\n***Error: " + msg;
	    	Print(s);
	}	
	static public void PrtWarn(String msg)
	{
	    	String s = "+++Warning: " + msg;
	    	Print(s);
	}	
	static public void die(String msg)
	{
   		String s = "***Abort execution: " + msg;
		Print(s);
		System.err.flush();
		System.exit(-1);
	}
	
	static public long getTime () {
	    return System.currentTimeMillis(); 
	}	
	static public void PrtDateMsg (String msg) {
	     Print(msg + " " + TimeHelpers.getDate());
	}		
    static public void PrtDateMsgTime (String msg, long t)
    {
        Print(msg + " " + TimeHelpers.getDate() + " Elapse time " + TimeHelpers.getElapsedTimeStr(t));
    }	
    static public void PrtSpMsgTime (int i, String msg, long t)
    {
	    	String sp = "";
	    	for (int j=0; j < i; j++) sp += "   ";
        sp += msg + TimeHelpers.getElapsedTimeStr(t);
        Print(sp);
    }     
    static public void PrtSpMsg (int i, String msg) {
	    	String sp = "";
	    	for (int j=0; j < i; j++) sp += "   ";
        sp += msg;
        Print(sp);
    }
    	
	static private long nTaskStart;
	static public void StartTime(String msg) {
		nTaskStart = CoreMain.getTime();
		PrtDateMsg(msg);
	}
	
	static public void EndTime(String msg) {
		PrtSpMsgTime(0, msg, nTaskStart);
		PrtSpMsg(0, " ");
	}
	// CAS add 2/7/15
	public static String removeCurrentPath(String file) {
		String path="";
		try {
			String cur = System.getProperty("user.dir");
			path = file.replace(cur, "");
		}
		catch (Exception e) {ErrorReport.prtReport(e, "removing current path from " + file); }
		return path;
	}
	// CAS add 2/8/15
	public static BufferedReader openGZIP(String file) {
		try {
			if (!file.endsWith(".gz")) {
				File f = new File (file);
				if (f.exists())
	    				return new BufferedReader ( new FileReader (f));
				else {
					f = new File (file + ".gz");
					if (f.exists()) file = file + ".gz";
					else ErrorReport.die("Cannot open file " + file);
				}
			}
			if (file.endsWith(".gz")) {
				FileInputStream fin = new FileInputStream(file);
				GZIPInputStream gzis = new GZIPInputStream(fin);
				InputStreamReader xover = new InputStreamReader(gzis);
				return new BufferedReader(xover);
			}
			else ErrorReport.die("Do not recognize file suffix: " + file);
		}
		catch (Exception e) {
			String f = removeCurrentPath(file);
	    		ErrorReport.die(e, "Cannot open file " + f+ " or with .gz suffix");
	    }
		return null;
	}
	public static boolean yesNo(String question)
	{
		if (noPrompt) return true;
		cntPrompts++;
		BufferedReader inLine = new BufferedReader(new InputStreamReader(System.in));

		System.err.print("?--" + question + " (y/n)? "); 
		try
		{
			String resp = inLine.readLine();
			if (resp.equalsIgnoreCase("y")) return true;
			else if (resp.equalsIgnoreCase("n")) return false;
			else {
				System.err.println("Could not understand response '" + resp + "' -- please enter again");
				System.err.print("?--" + question + " (y/n)? "); 
				resp = inLine.readLine();
				if (resp.equalsIgnoreCase("y")) return true;
				else return false;
			}
		} catch (Exception e)
		{
			ErrorReport.prtReport(e, "Could not read from keyboard");
			return false;
		}
	}
	private static boolean checkGODB(boolean dieflag) // true die, false live
	{
		boolean rc = true;
		String godbName = mProps.getProperty("JPAVE_GO_DB");
		if (godbName == null || godbName.equals(""))
		{
			rc = false;
			PrtSpMsg(1, "No GO tree");
			if (dieflag)  die("Set GO_tree in runSingleTCW before working with GOs");
		}
		else
		{
			try
			{
				PrtSpMsg(1, "GO tree = " + godbName);
				if (!DBConn.checkMysqlDB("runSingle ", sqlObj.PAVE_host,godbName,sqlObj.PAVE_user,sqlObj.PAVE_password))
				{
					if (!dieflag) PrtWarn("GO_tree " + godbName + " is missing; ignoring GO step\n");
					else die("GO_tree " + godbName + " not found");
					return false;
				}
				DBConn goDB = GO.connectToGODB(sqlObj.PAVE_host,sqlObj.PAVE_user,sqlObj.PAVE_password,godbName);
				if (goDB == null)
				{
					if (!dieflag) PrtWarn("GO_tree " + godbName + " is not current; ignoring GO step\n");
					else die("GO_tree " + godbName + " is not current");
					return false;
				}
				String upTbl = godbName + ".PAVE_Uniprot";
				ResultSet rs = goDB.executeQuery("select count(*) from " + upTbl);
				if (!rs.first()) 
				{
					rc = false;
					if (!dieflag) PrtWarn("GO_tree " + godbName + " is not current; ignoring GO step\n");
					else die("GO_tree " + godbName + " is not current");
				} 
				else CoreMain.PrtSpMsg(1,"Add GO terms\n");
			}
			catch(Exception e)
			{
				ErrorReport.die(e, "Fatal error reading GO_tree " + godbName);
			}		
		}
	
		return rc;
	}
	
	/**************************************************************/
	private static void Usage_exit () {
		System.err.println(
				"\nUsage:  execAnno <project_directory> -c [configuration file] -q\n"
				+ "  A project directory must be supplied and exist under /projects.\n" 
				+ "  The default configuration file is sTCW.cfg.\n" 
				+ "  An alterative configuration file may be supplied using -c.\n" 
				+ "  The project directory must contain the sTCW.cfg or alternative.\n"
				+ "-q Do not prompt. Assume 'yes' to all prompts.\n"
				+ "-s Regenerate Overview including species and annoDB counts\n" 
				+ "-G Add GO data only (annotation already done)\n"
				+ "-a Delete annotation\n"
				+ "or\n"
				+ "Usage: execAnno -p\n" +
				  "    List all sTCW.cfg annotator parameters\n");
			System.exit(-1);
	}
	/**************************************************************/
	private static void Parameter_exit () {
		System.err.println(
				"Parameters:\n" 
				+ "  \n"
				+ "PAVE_db                     = <String>   e.g. sTCW_demo\n"
				+ "PAVE_host                   = <String>   e.g. localhost\n"
				+ "PAVE_user                   = <String>\n"
				+ "PAVE_password               = <String>\n"	
				+ "AssemblyID                  = <String>   e.g. demo\n"
				+ "CPUs                        = <Integer>  default 1\n"	
				+ " \n"
				+ "For all UniTrans to Database computations:\n"
				+ "  JPAVE_unitrans_type       = <Integer>  default " + defaultUniTransType + 
						"       (0=all, 1=contig only, 2 singletons only)\n"
				+ "  JPAVE_flanking_region     = <Integer>  default " + defaultFlankingRegion + 
						"       (Length of flanking region of contained hit)\n" 
				+ "  JPAVE_DBdate               = <String> default DBfasta date (format YYYY-MM-DD)\n"
				+ "             This will be used for all JPAVE_DBfasta_n if none is provided for a given _n\n"
				+ "             If no DBdate or DBdate_n is provided, the date of the fasta file is used\n"
				+ "  JPAVE_min_bitscore         = <Integer> minimum bitscore allowed for E-value=0.0\n"		
				+ "For n = 0 to " + numDB + ":\n"
				+ "  JPAVE_unitrans_DBblast_n   = <String>               (BLAST file of UniTran to DBfasta_n results)\n"
				+ "  JPAVE_DBfasta_n            = <String>               (Database in FASTA format)\n"
				+ "  JPAVE_DBargs_n             = <String>  defaults depend on blast+ vs legecy blast\n"
				+ "  JPAVE_DBtaxo_n             = <String>               (Taxonomy of UniProt or Nucleotide database)\n"
				+ "  JPAVE_DBdate_n             = <String>               (See JPAVE_DBdate)\n"
				+ "  JPAVE_unitrans_subset_n    = <yes/no>  default no   (yes: only Blast against UniTrans with no hits)\n"
				+ " \n"
				+ "For Pairwise computations:\n"
				+ "  JPAVE_pairs_limit           = <Integer>  default " + defaultPairsLimit + " (Use 0 for no pairs)\n"
				+ "  JPAVE_unitrans_selfblast     = <String>               (BLAST file of Sequences against themselves)\n"
				+ "  JPAVE_selfblast_args         = <String>  defaults depend on blast+ vs legecy blast\n"
				+ "  JPAVE_unitrans_tselfblast    = <String>               (BLAST file of Sequences against themselves)\n" 
				+ "  JPAVE_tselfblast_args        = <String>  default  depend on blast+ vs legecy blast\n"
				+ "For representative file created using pairwise results:\n"
				+ "  JPAVE_rep_overlap        	 = <Integer>  default " + defaultRepOverlap + "\n"
				+ "  JPAVE_rep_similarity       	= <Integer>  default " + defaultRepSimilarity + "\n"
				);
		System.exit(-1);
	}

	/*************************************************************
	 * Get and put methods
	 *************************************************************/
	
	static public String getProjPath() {return projPath;}
	static public String getProjName() {return projName;}
	static public String getAssemblyID() {return assemblyID;}
	static public String getBlastDir() { return uniBlastDir;}
	static public int getCPUs() {return nCPUs;}
	// Annotation settings

	static private HostsCfg hosts = null;
	static private String pave_db = null;
	static private String assemblyID = null;
	
	static private DoBlast blastObj = null;
	static private DoUniProt uniObj = null;
	static private CoreAnnotator annoObj = null;
	static private CoreDBWrapper sqlObj = null;
	
	static public PrintWriter logFileObj;
	
	static private int  nCPUs = 1;
	static private PaveProps mProps;
	static private String projPath = null;
	static private String projName = null;
	static private String uniBlastDir = null;
	
	static public boolean bDelAnno = false; // set to delete annotation
	static private boolean bdoAnno=true; // don't print parameters 
	static private int cntPrompts=0; // counted in yesno method
	static public boolean noPrompt = false;
	static private boolean isProtein=false;
}
